package com.jdsh.encrypt.utils;


import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.util.Arrays;

/**
 * SM2公钥加密算法实现 包括 -签名,验签 -密钥交换 -公钥加密,私钥解密
 *
 * @author Potato
 *
 */
public class SM2 {
    private static BigInteger n = new BigInteger(
            "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "7203DF6B" + "21C6052B" + "53BBF409" + "39D54123", 16);
    private static BigInteger p = new BigInteger(
            "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFF", 16);
    private static BigInteger a = new BigInteger(
            "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFC", 16);
    private static BigInteger b = new BigInteger(
            "28E9FA9E" + "9D9F5E34" + "4D5A9E4B" + "CF6509A7" + "F39789F5" + "15AB8F92" + "DDBCBD41" + "4D940E93", 16);
    private static BigInteger gx = new BigInteger(
            "32C4AE2C" + "1F198119" + "5F990446" + "6A39C994" + "8FE30BBF" + "F2660BE1" + "715A4589" + "334C74C7", 16);
    private static BigInteger gy = new BigInteger(
            "BC3736A2" + "F4F6779C" + "59BDCEE3" + "6B692153" + "D0A9877C" + "C62A4740" + "02DF32E5" + "2139F0A0", 16);
    private static ECDomainParameters ecc_bc_spec;
    private static int w = (int) Math.ceil(n.bitLength() * 1.0 / 2) - 1;
    private static BigInteger _2w = new BigInteger("2").pow(w);
    private static final int DIGEST_LENGTH = 32;

    private static SecureRandom random = new SecureRandom();
    private static ECCurve.Fp curve;
    private static ECPoint G;
    private static boolean debug = false;

    public boolean isDebug() {
        return debug;
    }

    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    /**
     * 产生随机数
     * @return
     */
    public static String getAESRandomKey() {
        SecureRandom random = new SecureRandom();
        long randomKey = random.nextLong();
        return String.valueOf(randomKey);
    }
    /**
     * 以16进制打印字节数组
     *
     * @param b
     */
    public  static String  printHexString(byte[] b) {
        String str="";
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            str=str+hex;
            //System.out.print(hex);
        }
        //System.out.println();
    return str;
    }

    /**
     * 将数组转换为字符串工具类
     * @param src
     * @return
     */
    public static String bytesToHexString(byte[] src) {

        StringBuilder stringBuilder = new StringBuilder("");

        if (src == null || src.length <= 0) {

            return null;

        }

        for (int i = 0; i < src.length; i++) {

            int v = src[i] & 0xFF;

            String hv = Integer.toHexString(v);

            if (hv.length() < 2) {

                stringBuilder.append(0);

            }

            stringBuilder.append(hv);

        }

        return stringBuilder.toString();

    }

    /**
     * 将字符串转换为数组
     * @param hexString
     * @return
     */

    public static byte[] hexStringToBytes(String hexString) {

        if (hexString == null || hexString.equals("")) {

            return null;

        }

        hexString = hexString.toUpperCase();

        int length = hexString.length() / 2;

        char[] hexChars = hexString.toCharArray();

        byte[] d = new byte[length];

        for (int i = 0; i < length; i++) {

            int pos = i * 2;

            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));


        }

        return d;

    }

    private static byte charToByte(char c) {

        return (byte) "0123456789ABCDEF".indexOf(c);

    }


    /**
     * 随机数生成器
     *
     * @param max
     * @return
     */
    private static BigInteger random(BigInteger max) {

        BigInteger r = new BigInteger(256, random);
        // int count = 1;

        while (r.compareTo(max) >= 0) {
            r = new BigInteger(128, random);
            // count++;
        }

        // System.out.println("count: " + count);
        return r;
    }

    /**
     * 判断字节数组是否全0
     *
     * @param buffer
     * @return
     */
    private static boolean allZero(byte[] buffer) {
        for (int i = 0; i < buffer.length; i++) {
            if (buffer[i] != 0)
                return false;
        }
        return true;
    }


    /**
     * 公钥加密
     *
     * @param data
     *            加密原文
     * @param key
     *            公钥
     * @return
     */
    public static String encryptWithBC(String data, String key) throws Exception {

        ByteBuffer buffer = ByteBuffer.allocate(32);
        buffer.put(key.getBytes());
        KeyParameter kp = new KeyParameter(buffer.array());
        byte[] bytes = data.getBytes("UTF8");

        CBCBlockCipher aes = new CBCBlockCipher(new AESEngine());
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(aes, new PKCS7Padding());
        cipher.init(true, kp);

        byte[] output = new byte[cipher.getOutputSize(bytes.length)];
        int len = cipher.processBytes(bytes, 0, bytes.length, output, 0);
        cipher.doFinal(output, len);

        return Base64.encodeBase64String(output);
    }

    /**
     * 用随机k对原始数据进行解密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */


    public static String decryptWithBC(String data, String key) throws Exception {
        ByteBuffer buffer = ByteBuffer.allocate(32);
        buffer.put(key.getBytes());
        KeyParameter kp = new KeyParameter(buffer.array());

        byte[] bytes = Base64.decodeBase64(data);

        CBCBlockCipher aes = new CBCBlockCipher(new AESEngine());
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(aes, new PKCS7Padding());
        cipher.init(false, kp);

        byte[] output = new byte[cipher.getOutputSize(bytes.length)];
        int len = cipher.processBytes(bytes, 0, bytes.length, output, 0);
        int len2 = cipher.doFinal(output, len);
        byte rawData[] = new byte[len+len2];
        System.arraycopy(output, 0, rawData, 0, rawData.length);
        String plainData = new String(rawData, Charset.forName("utf-8"));
        return plainData;
    }

    public static  String encrypt(String input, ECPoint publicKey) {

        byte[] inputBuffer = input.getBytes();
        if (debug)
            printHexString(inputBuffer);

        byte[] C1Buffer;
        ECPoint kpb;
        byte[] t;
        do {
            /* 1 产生随机数k，k属于[1, n-1] */
            BigInteger k = random(n);
            /*if (debug) {
                System.out.print("k: ");
                printHexString(k.toByteArray());
            }*/

            /* 2 计算椭圆曲线点C1 = [k]G = (x1, y1) */
            ECPoint C1 = G.multiply(k);
            C1Buffer = C1.getEncoded(false);
            /*if (debug) {
                System.out.print("C1: ");
                printHexString(C1Buffer);
            }*/

            /*
             * 3 计算椭圆曲线点 S = [h]Pb
             */
            BigInteger h = ecc_bc_spec.getH();
            if (h != null) {
                ECPoint S = publicKey.multiply(h);
                if (S.isInfinity())
                    throw new IllegalStateException();
            }

            /* 4 计算 [k]PB = (x2, y2) */
            kpb = publicKey.multiply(k).normalize();

            /* 5 计算 t = KDF(x2||y2, klen) */
            byte[] kpbBytes = kpb.getEncoded(false);
            t = KDF(kpbBytes, inputBuffer.length);
            // DerivationFunction kdf = new KDF1BytesGenerator(new
            // ShortenedDigest(new SHA256Digest(), DIGEST_LENGTH));
            //
            // t = new byte[inputBuffer.length];
            // kdf.init(new ISO18033KDFParameters(kpbBytes));
            // kdf.generateBytes(t, 0, t.length);
        } while (allZero(t));

        /* 6 计算C2=M^t */
        byte[] C2 = new byte[inputBuffer.length];
        for (int i = 0; i < inputBuffer.length; i++) {
            C2[i] = (byte) (inputBuffer[i] ^ t[i]);
        }

        /* 7 计算C3 = Hash(x2 || M || y2) */
        byte[] C3 = sm3hash(kpb.getXCoord().toBigInteger().toByteArray(), inputBuffer,
                kpb.getYCoord().toBigInteger().toByteArray());

        /* 8 输出密文 C=C1 || C2 || C3 */

        byte[] encryptResult = new byte[C1Buffer.length + C2.length + C3.length];

        System.arraycopy(C1Buffer, 0, encryptResult, 0, C1Buffer.length);
        System.arraycopy(C2, 0, encryptResult, C1Buffer.length, C2.length);
        System.arraycopy(C3, 0, encryptResult, C1Buffer.length + C2.length, C3.length);

       /* if (debug) {
            System.out.print("密文: ");
            printHexString(encryptResult);
        }*/

        return bytesToHexString(encryptResult);
    }
//todo
    /**
     * 私钥解密
     *
     * @param
     *
     * @param privateKey
     *            解密私钥
     * @return
     */
    public static  String decrypt(String encryptDataStr, BigInteger privateKey) {

        byte[] encryptData=hexStringToBytes(encryptDataStr);
        /*if (debug)
            System.out.println("encryptData length: " + encryptData.length);*/

        byte[] C1Byte = new byte[65];
        System.arraycopy(encryptData, 0, C1Byte, 0, C1Byte.length);

        ECPoint C1 = curve.decodePoint(C1Byte).normalize();

        /*
         * 计算椭圆曲线点 S = [h]C1 是否为无穷点
         */
        BigInteger h = ecc_bc_spec.getH();
        if (h != null) {
            ECPoint S = C1.multiply(h);
            if (S.isInfinity())
                throw new IllegalStateException();
        }
        /* 计算[dB]C1 = (x2, y2) */
        ECPoint dBC1 = C1.multiply(privateKey).normalize();

        /* 计算t = KDF(x2 || y2, klen) */
        byte[] dBC1Bytes = dBC1.getEncoded(false);
        int klen = encryptData.length - 65 - DIGEST_LENGTH;
        byte[] t = KDF(dBC1Bytes, klen);
        // DerivationFunction kdf = new KDF1BytesGenerator(new
        // ShortenedDigest(new SHA256Digest(), DIGEST_LENGTH));
        // if (debug)
        // System.out.println("klen = " + klen);
        // kdf.init(new ISO18033KDFParameters(dBC1Bytes));
        // kdf.generateBytes(t, 0, t.length);

        if (allZero(t)) {
            System.err.println("all zero");
            throw new IllegalStateException();
        }

        /* 5 计算M'=C2^t */
        byte[] M = new byte[klen];
        for (int i = 0; i < M.length; i++) {
            M[i] = (byte) (encryptData[C1Byte.length + i] ^ t[i]);
        }
        if (debug)
            printHexString(M);

        /* 6 计算 u = Hash(x2 || M' || y2) 判断 u == C3是否成立 */
        byte[] C3 = new byte[DIGEST_LENGTH];

        /*if (debug)
            try {
                System.out.println("M = " + new String(M, "UTF8"));
            } catch (UnsupportedEncodingException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }*/

        System.arraycopy(encryptData, encryptData.length - DIGEST_LENGTH, C3, 0, DIGEST_LENGTH);
        byte[] u = sm3hash(dBC1.getXCoord().toBigInteger().toByteArray(), M,
                dBC1.getYCoord().toBigInteger().toByteArray());
        if (Arrays.equals(u, C3)) {
           /* if (debug)
                System.out.println("解密成功");*/
            try {
                return new String(M, "UTF8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return null;
        } else {
            /*if (debug) {
                System.out.print("u = ");
                printHexString(u);
                System.out.print("C3 = ");
                printHexString(C3);
                System.err.println("解密验证失败");
            }*/
            return null;
        }

    }



    /**
     * 判断是否在范围内
     *
     * @param param
     * @param min
     * @param max
     * @return
     */
    private static boolean between(BigInteger param, BigInteger min, BigInteger max) {
        if (param.compareTo(min) >= 0 && param.compareTo(max) < 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判断生成的公钥是否合法
     *
     * @param publicKey
     * @return
     */
    private static boolean checkPublicKey(ECPoint publicKey) {

        if (!publicKey.isInfinity()) {

            BigInteger x = publicKey.getXCoord().toBigInteger();
            BigInteger y = publicKey.getYCoord().toBigInteger();

            if (between(x, new BigInteger("0"), p) && between(y, new BigInteger("0"), p)) {

                BigInteger xResult = x.pow(3).add(a.multiply(x)).add(b).mod(p);

                /*if (debug)
                    System.out.println("xResult: " + xResult.toString());*/

                BigInteger yResult = y.pow(2).mod(p);

               /* if (debug)
                    System.out.println("yResult: " + yResult.toString());*/

                if (yResult.equals(xResult) && publicKey.multiply(n).isInfinity()) {
                    return true;
                }
            }
        }
        return false;
    }



    /**
     * 生成密钥对
     *
     * @return
     */
    public static SM2KeyPair generateKeyPair() {
         BigInteger  d = random(n.subtract(new BigInteger("1")));//n与1做差

        SM2KeyPair keyPair = new SM2KeyPair(G.multiply(d).normalize(), d);//用私钥作为参数，经过算法生成公钥

        if (checkPublicKey(keyPair.getPublicKey())) {
            /*if (debug)
                System.out.println("generate key successfully");*/
            return keyPair;
        } else {
            if (debug)
                System.err.println("generate key failed");
            return null;
        }
    }

   public SM2() {
        curve = new ECCurve.Fp(p, // q
                a, // a
                b); // b
        G = curve.createPoint(gx, gy);
        ecc_bc_spec = new ECDomainParameters(curve, G, n);
    }

    public SM2(boolean debug) {
        this();
        this.debug = debug;
    }

    /**
     * 导出公钥到本地
     *
     * @param publicKey
     * @param path
     */
    public void exportPublicKey(ECPoint publicKey, String path) {
        File file = new File(path);
        try {
            if (!file.exists())
                file.createNewFile();
            byte buffer[] = publicKey.getEncoded(false);
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(buffer);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从本地导入公钥
     *
     * @param path
     * @return
     */
    public static ECPoint importPublicKey(String path) {
        File file = new File(path);
        try {
            if (!file.exists())
                return null;
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            byte buffer[] = new byte[16];
            int size;
            while ((size = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, size);
            }
            fis.close();
            byte[] var1=baos.toByteArray();
            return curve.decodePoint(var1);
        } catch (IOException e) {
            e.printStackTrace();


        }
        return null;
    }

    /**
     * 导出私钥到本地
     *
     * @param privateKey
     * @param path
     */
   /* public void exportPrivateKey(BigInteger privateKey, String path) {
        File file = new File(path);
        try {
            if (!file.exists())
                file.createNewFile();
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
            oos.writeObject(privateKey);
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
*/
    /**
     * 从本地导入私钥
     *
     * @param path
     * @return
     */
    public static BigInteger importPrivateKey(String path) {
       File file = new File(path);
        try {
            if (!file.exists())
                return null;
            FileInputStream fis = new FileInputStream(file);
            ObjectInputStream ois = new ObjectInputStream(fis);
            BigInteger res = (BigInteger) (ois.readObject());
            ois.close();
            fis.close();
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 字节数组拼接
     *
     * @param params
     * @return
     */
    private static byte[] join(byte[]... params) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] res = null;
        try {
            for (int i = 0; i < params.length; i++) {
                baos.write(params[i]);
            }
            res = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * sm3摘要
     *
     * @param params
     * @return
     */
    private static byte[] sm3hash(byte[]... params) {
        byte[] res = null;
        try {
            res = SM3.hash(join(params));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 取得用户标识字节数组
     *
     * @param IDA
     * @param aPublicKey
     * @return
     */
    private static byte[] ZA(String IDA, ECPoint aPublicKey) {
        byte[] idaBytes = IDA.getBytes();
        int entlenA = idaBytes.length * 8;
        byte[] ENTLA = new byte[] { (byte) (entlenA & 0xFF00), (byte) (entlenA & 0x00FF) };
        byte[] ZA = sm3hash(ENTLA, idaBytes, a.toByteArray(), b.toByteArray(), gx.toByteArray(), gy.toByteArray(),
                aPublicKey.getXCoord().toBigInteger().toByteArray(),
                aPublicKey.getYCoord().toBigInteger().toByteArray());
        return ZA;
    }

    /**
     * 签名
     *
     * @param M
     *            签名信息
     * @param IDA
     *            签名方唯一标识
     * @param keyPair
     *            签名方密钥对
     * @return 签名
     */
    public static Signature sign(String M, String IDA, SM2KeyPair keyPair) {
        byte[] ZA = ZA(IDA, keyPair.getPublicKey());
        byte[] M_ = join(ZA, M.getBytes());
        BigInteger e = new BigInteger(1, sm3hash(M_));
        // BigInteger k = new BigInteger(
        // "6CB28D99 385C175C 94F94E93 4817663F C176D925 DD72B727 260DBAAE
        // 1FB2F96F".replace(" ", ""), 16);
        BigInteger k;
        BigInteger r;
        do {
            k = random(n);
            ECPoint p1 = G.multiply(k).normalize();
            BigInteger x1 = p1.getXCoord().toBigInteger();
            r = e.add(x1);
            r = r.mod(n);
        } while (r.equals(BigInteger.ZERO) || r.add(k).equals(n));

        BigInteger s = ((keyPair.getPrivateKey().add(BigInteger.ONE).modInverse(n))
                .multiply((k.subtract(r.multiply(keyPair.getPrivateKey()))).mod(n))).mod(n);

        return new Signature(r, s);
    }

    /**
     * 验签
     *
     * @param M
     *            签名信息
     * @param signature
     *            签名
     * @param IDA
     *            签名方唯一标识
     * @param aPublicKey
     *            签名方公钥
     * @return true or false
     */
    public static  boolean verify(String M, Signature signature, String IDA, ECPoint aPublicKey) {
        if (!between(signature.getR(), BigInteger.ONE, n))
            return false;
        if (!between(signature.getS(), BigInteger.ONE, n))
            return false;

        byte[] M_ = join(ZA(IDA, aPublicKey), M.getBytes());
        BigInteger e = new BigInteger(1, sm3hash(M_));
        BigInteger t = signature.getR().add(signature.getS()).mod(n);// rsn=BigInteger

        if (t.equals(BigInteger.ZERO))
            return false;

        ECPoint p1 = G.multiply(signature.getS()).normalize();
        ECPoint p2 = aPublicKey.multiply(t).normalize();
        BigInteger x1 = p1.add(p2).normalize().getXCoord().toBigInteger();
        BigInteger R = e.add(x1).mod(n);
        if (R.equals(signature.getR()))
            return true;
        return false;
    }

    /**
     * 密钥派生函数
     *
     * @param Z
     * @param klen
     *            生成klen字节数长度的密钥
     * @return
     */
    private static byte[] KDF(byte[] Z, int klen) {
        int ct = 1;
        int end = (int) Math.ceil(klen * 1.0 / 32);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            for (int i = 1; i < end; i++) {
                baos.write(sm3hash(Z, SM3.toByteArray(ct)));
                ct++;
            }
            byte[] last = sm3hash(Z, SM3.toByteArray(ct));
            if (klen % 32 == 0) {
                baos.write(last);
            } else
                baos.write(last, 0, klen % 32);
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 传输实体类
     *
     * @author Potato
     *
     *//*
    public static class TransportEntity implements Serializable {
        final byte[] R; //R点
        final byte[] S; //验证S
        final byte[] Z; //用户标识
        final byte[] K; //公钥

        public TransportEntity(byte[] r, byte[] s,byte[] z,ECPoint pKey) {
            R = r;
            S = s;
            Z=z;
            K=pKey.getEncoded(false);//对公钥进行加密
        }
       *//*  public ECPoint toECPoint(byte[] K){

            return
         }*//*

        @Override
        public String toString() {
            return "TransportEntity{" +
                    "R=" + Arrays.toString(R) +
                    ", S=" + Arrays.toString(S) +
                    ", Z=" + Arrays.toString(Z) +
                    ", K=" + Arrays.toString(K) +
                    '}';
        }
    }

    *//**
     * 密钥协商辅助类
     *
     * @author Potato
     *
     *//*
    public static class KeyExchange {
        BigInteger rA;
        ECPoint RA;
        ECPoint V;
        ECPoint otherPK;
        byte[] Z;
        byte[] key;
        String ID;
        SM2KeyPair keyPair;

        public KeyExchange(String ID,SM2KeyPair keyPair) {
            this.ID=ID;
            this.keyPair = keyPair;
            this.Z=ZA(ID, keyPair.getPublicKey());
        }

        public byte[] getKey() {
            return key;
        }

        public ECPoint getOtherPK() {
            return otherPK;
        }

        *//**
         * 密钥协商发起第一步
         *
         * @return
         *//*
        public TransportEntity keyExchange_1() {
            rA = random(n);
            // rA=new BigInteger("83A2C9C8 B96E5AF7 0BD480B4 72409A9A 327257F1
            // EBB73F5B 073354B2 48668563".replace(" ", ""),16);
            RA = G.multiply(rA).normalize();
            return new TransportEntity(RA.getEncoded(false), null,Z,keyPair.getPublicKey());
        }

        *//**
         * 密钥协商响应方
         *
         * @param entity 传输实体
         * @return
         *//*
        public TransportEntity keyExchange_2(TransportEntity entity) {

            BigInteger rB = random(n);
            // BigInteger rB=new BigInteger("33FE2194 0342161C 55619C4A 0C060293
            // D543C80A F19748CE 176D8347 7DE71C80".replace(" ", ""),16);
            ECPoint RB = G.multiply(rB).normalize();

            this.rA=rB;
            this.RA=RB;

            BigInteger x2 = RB.getXCoord().toBigInteger();//B根据自己新生成的公钥生成一个椭圆上的点   X2
            x2 = _2w.add(x2.and(_2w.subtract(BigInteger.ONE)));//对x2进行椭圆算法*
            BigInteger tB = keyPair.getPrivateKey().add(x2.multiply(rB)).mod(n);//本身的私钥和x2进行混合运算
            ECPoint RA = curve.decodePoint(entity.R).normalize();//传过来的A的公钥

            BigInteger x1 = RA.getXCoord().toBigInteger();//根据A的公钥生成一个椭圆上的点 X1
            x1 = _2w.add(x1.and(_2w.subtract(BigInteger.ONE)));//对X1 进行椭圆算法

            ECPoint aPublicKey=curve.decodePoint(entity.K).normalize();//获得A的公钥
            this.otherPK=aPublicKey;
            System.out.println("获得英大的公钥:"+aPublicKey.toString());
            ECPoint temp = aPublicKey.add(RA.multiply(x1).normalize()).normalize();//用A的公钥，x1,和传过来的A的公钥 进行算法 得出temp
            ECPoint V = temp.multiply(ecc_bc_spec.getH().multiply(tB)).normalize();//temp 和 tb（本身私钥和x2） 进行验证 V
            if (V.isInfinity())
                throw new IllegalStateException();
            this.V=V;

            byte[] xV = V.getXCoord().toBigInteger().toByteArray();
            byte[] yV = V.getYCoord().toBigInteger().toByteArray();
            byte[] KB = KDF(join(xV, yV, entity.Z, this.Z), 16);//V的坐标和A的用户标识和B的用户标识   用KDF生成第一个密钥（戴验证）
            key = KB;

            System.out.print("B初步协商的密钥为:");

            printHexString(KB);


            byte[] sB = sm3hash(key, yV,
                    sm3hash(xV, entity.Z, this.Z, RA.getXCoord().toBigInteger().toByteArray(),
                            RA.getYCoord().toBigInteger().toByteArray(), RB.getXCoord().toBigInteger().toByteArray(),
                            RB.getYCoord().toBigInteger().toByteArray()));//消息摘要  待定


            return new TransportEntity(RB.getEncoded(false), sB,this.Z,keyPair.getPublicKey());//RB 公钥加密  消息摘要  B公司标识 B的公钥
        }

        *//**
         * 密钥协商发起方第二步
         *
         * @param entity 传输实体
         *//*
        public TransportEntity keyExchange_3(TransportEntity entity) {

            BigInteger x1 = RA.getXCoord().toBigInteger();//  模仿keyExchange_2的生成随机数
            x1 = _2w.add(x1.and(_2w.subtract(BigInteger.ONE)));

            BigInteger tA = keyPair.getPrivateKey().add(x1.multiply(rA)).mod(n);//本身的私钥和x1进行混合运算 生成tA
            ECPoint RB = curve.decodePoint(entity.R).normalize();//传过来的对方的公钥

            BigInteger x2 = RB.getXCoord().toBigInteger();//穿过来的B 公钥
            x2 = _2w.add(x2.and(_2w.subtract(BigInteger.ONE)));//生成x2

            ECPoint bPublicKey=curve.decodePoint(entity.K).normalize();//获取对方的公钥
            System.out.println("获得经代公司的公钥"+bPublicKey.toString());
            this.otherPK=bPublicKey;
            ECPoint temp = bPublicKey.add(RB.multiply(x2).normalize()).normalize();//根据目前对方传过来的参数生成一个待验证的temp
            ECPoint U = temp.multiply(ecc_bc_spec.getH().multiply(tA)).normalize();//根据参数生成的temp 跟己方根据算法参数生成的temp 对比
            if (U.isInfinity())
                throw new IllegalStateException();
            this.V=U;

            byte[] xU = U.getXCoord().toBigInteger().toByteArray();
            byte[] yU = U.getYCoord().toBigInteger().toByteArray();
            byte[] KA = KDF(join(xU, yU,
                    this.Z, entity.Z), 16);
            key = KA;
            System.out.print("英大协商生成的密钥为:");
            printHexString(KA);
            byte[] s1= sm3hash(key, yU,
                    sm3hash(xU, this.Z, entity.Z, RA.getXCoord().toBigInteger().toByteArray(),
                            RA.getYCoord().toBigInteger().toByteArray(), RB.getXCoord().toBigInteger().toByteArray(),
                            RB.getYCoord().toBigInteger().toByteArray()));
            if(Arrays.equals(entity.S, s1))
                System.out.println("B->A 密钥确认成功");
            else
                System.out.println("B->A 密钥确认失败");
            byte[] sA= sm3hash(key, yU,
                    sm3hash(xU, this.Z, entity.Z, RA.getXCoord().toBigInteger().toByteArray(),
                            RA.getYCoord().toBigInteger().toByteArray(), RB.getXCoord().toBigInteger().toByteArray(),
                            RB.getYCoord().toBigInteger().toByteArray()));

            return new TransportEntity(RA.getEncoded(false), sA,this.Z,keyPair.getPublicKey());//自己的公钥，消息摘要（由自己方生成的密钥进行SM3拼凑），自己的标识，自己的公钥
        }

        *//**
         * 密钥确认最后一步
         *
         * @param entity 传输实体
         *//*
        public TransportEntity keyExchange_4(TransportEntity entity) {
            byte[] xV = V.getXCoord().toBigInteger().toByteArray();
            byte[] yV = V.getYCoord().toBigInteger().toByteArray();
            ECPoint RA = curve.decodePoint(entity.R).normalize();//由对方传来的对方的公钥
            byte[] s2= sm3hash(key, yV,
                    sm3hash(xV, entity.Z, this.Z, RA.getXCoord().toBigInteger().toByteArray(),
                            RA.getYCoord().toBigInteger().toByteArray(), this.RA.getXCoord().toBigInteger().toByteArray(),
                            this.RA.getYCoord().toBigInteger().toByteArray()));
            if(Arrays.equals(entity.S, s2))
                System.out.println("A->B 密钥确认成功");
            else
                System.out.println("A->B 密钥确认失败");
            return entity;
        }




    }
*/

}